#include "VideoFilter.h"
#include <initguid.h>
#include "stdio.h"

//7fec15ce-5cf1-42ea-ba85-0360b598e766
DEFINE_GUID(CLSID_VideoFilter,
			0x7fec15ce, 0x5cf1, 0x42ea, 0xba, 0x85, 0x03, 0x60, 0xb5, 0x98, 0xe7, 0x66);

VideoFilter::VideoFilter(HRESULT *phr, bool hFlip) :
	CTransInPlaceFilter(L"VideoFilter", NULL, CLSID_VideoFilter, phr),
	hFlip(hFlip), queryBuffer(false)
{
	AddRef();
}

void VideoFilter::StartQueryBuffer(BYTE *buf, int bufLen)
{
	this->buf = buf;
	this->bufLen = bufLen;
	queryBuffer = true;
}

BYTE *VideoFilter::GetBuffer(int &bufLen)
{
	if (queryBuffer)
	{
		bufLen = 0;
		return NULL;
	}
	bufLen = this->bufLen;
	return buf;
}

void Flip1(BYTE *p, int width, int height)
{
	for (BYTE *pz = p + width*height; p<pz; p+=width)
	{
		for (int x=0, xz=width/2; x<xz; x++)
		{
			BYTE *tp = p + width - x - 1;
			BYTE t = p[x];
			p[x] = *tp;
			*tp = t;
		}
	}
}

void Flip3(BYTE *p, int width, int height)
{
	for (BYTE *pz = p + width*height; p<pz; p+=width)
	{
		for (int x=0, xz=width/2; x<xz; x+=3)
		{
			{
				short *ps = (short*)(p + x);
				short *ps2 = (short*)(p + width - x - 3);
				short t = *ps;
				*ps = *ps2;
				*ps2 = t;
			}
			
			{
				BYTE *pb = p + x + 2;
				BYTE *pb2 = p + width - x - 1;
				BYTE t = *pb;
				*pb = *pb2;
				*pb2 = t;
			}
		}
	}
}

void Flip4(BYTE *p, int width, int height)
{
	int iw = width/4;
	for (BYTE *pz = p + width*height; p<pz; p+=width)
	{
		int *ip = (int*)p;
		for (int x=0, xz=iw/2; x<xz; x++)
		{
			int *tp = ip + iw - x - 1;
			int t = ip[x];
			ip[x] = *tp;
			*tp = t;
		}
	}
}

HRESULT VideoFilter::Transform(IMediaSample *pSample)
{
	if (queryBuffer || hFlip)
	{
		BYTE *pBuf;
		pSample->GetPointer(&pBuf);
		long bufSize = pSample->GetSize();
		if (queryBuffer)
		{
			if (bufLen<bufSize)
			{
				bufLen = bufSize;
				buf = new BYTE[bufLen];
			}
			memcpy(buf, pBuf, bufSize);
			queryBuffer = false;
		}
		if (hFlip)
		{
			if (videoType==(GUID)FOURCCMap('024I') || videoType==MEDIASUBTYPE_IYUV || videoType==MEDIASUBTYPE_YV12)
			{
				Flip1(pBuf, videoWidth, videoHeight);
				Flip1(pBuf + videoWidth*videoHeight, videoWidth/2, videoHeight/2);
				Flip1(pBuf + videoWidth*videoHeight*5/4, videoWidth/2, videoHeight/2);
			}
			else
			if (videoType==MEDIASUBTYPE_YUY2 || videoType==MEDIASUBTYPE_YUYV)
			{
				BYTE *p = pBuf;
				int width = videoWidth*2;
				for (BYTE *pz = p + width*videoHeight; p<pz; p+=width)
				{
					for (int x=0, xz=width/2; x<xz; x+=4)
					{
						BYTE t = p[x];
						p[x] = p[width - x - 2];
						p[width - x - 2] = t;
						t = p[x + 2];
						p[x+1] = p[width - x - 4];
						p[width - x - 4] = t;

						t = p[x + 1];
						p[x + 1] = p[width - x - 3];
						p[width - x - 3] = t;
						t = p[x + 3];
						p[x + 3] = p[width - x - 1];
						p[width - x - 1] = t;
					}
				}
			}
			else
			if (videoType==MEDIASUBTYPE_RGB24)
			{
				Flip3(pBuf, videoWidth*3, videoHeight);
			}
			else
			if (videoType==MEDIASUBTYPE_RGB32)
			{
				Flip4(pBuf, videoWidth*4, videoHeight);
			}
		}
	}
	return S_OK;
}

HRESULT VideoFilter::CheckInputType(const CMediaType* mtIn) 
{ 
	return S_OK;
}

HRESULT VideoFilter::SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt)
{
	videoType = pmt->subtype;
	VIDEOINFOHEADER *pHeader = (VIDEOINFOHEADER*)pmt->pbFormat;
	videoWidth = pHeader->bmiHeader.biWidth;
	videoHeight = pHeader->bmiHeader.biHeight;
	return S_OK;
}